#pragma once

#include <httplib.h>
#include <spdlog/spdlog.h>
#include "../util/server.hh"
#include "../util/response.hh"
#include "../util/util.hh"
#include "../poco/vocabulary.hh"
#include "../service/vocabulary-service.hh"

namespace WordMemory
{
    class VocabularyController
    {
    public:
        static void registerBusiness()
        {
            Server::instance().regist<Server::BusType::GET>("/api/vocabularies", getVocabularies);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies", addVocabulary);
            Server::instance().regist<Server::BusType::GET>("/api/vocabularies/([0-9]+)/words", getWordsVocabularyId);
            Server::instance().regist<Server::BusType::GET>("/api/vocabularies/default", getDefaultVocabulary);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies/default", setDefaultVocabulary);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies/favorites/add", addWordToFavorites);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies/favorites/remove", removeWordFromFavorites);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies/mistakes/add", addWordToMistakesBook);
            Server::instance().regist<Server::BusType::POST>("/api/vocabularies/mistakes/remove", removeWordFromMistakesBook);
            Server::instance().regist<Server::BusType::GET>("/api/vocabularies/mistakes/words", getMistakesBookWords);

            spdlog::info("Vocabulary controller registered");
        }

    private:
        static void getVocabularies(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                int user_id = std::stoi(req.get_param_value("user_id"));
                auto vocabularies = VocabularyService::instance().getVocabularies(user_id);

                Json::Value data(Json::arrayValue);
                for (const auto& vocab : vocabularies)
                    data.append(vocab.to_json());

                res.status = 200;
                res.set_content(ResponseMessage::success(data), "application/json");

                spdlog::info("Fetched vocabularies for user_id {}", user_id);
            }
            catch (const ServerExcept&)
            {
                res.status = 400;
                res.set_content(ResponseMessage::error("Invalid user_id"), "application/json");

                spdlog::error("Invalid user_id: {}", req.get_param_value("user_id"));
            }
        }

        static void addVocabulary(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value json = JsonUtil::deserialize(req.body);

                int user_id = json["user_id"].asInt();
                std::string name = json["name"].asString();

                Vocabulary vocabulary = VocabularyService::instance().addVocabulary(user_id, name);

                res.status = 200;
                res.set_content(ResponseMessage::success(vocabulary.to_json()), "application/json");

                spdlog::info("Added vocabulary {} for user_id {}", name, user_id);
            }
            catch (const ServerExcept& e)
            {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");

                spdlog::error("Failed to add vocabulary: {}", e.what());
            }
            catch (const UtilExcept& e)
            {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");

                spdlog::error("Failed to parse JSON: {}", e.what());
            }
        }

        static void getWordsVocabularyId(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                int vocab_id = std::stoi(req.matches[1]);
                int user_id = std::stoi(req.get_param_value("user_id"));

                auto words = VocabularyService::instance().getWordsOnlyTextByVocabularyId(vocab_id);

                Json::Value data(Json::arrayValue);
                for (const auto& word : words) {
                    data.append(
                        WordService::instance().getWordWithFavoriteStatus(user_id, word.word)
                        .to_json());
                }

                res.status = 200;
                res.set_content(ResponseMessage::success(data), "application/json");

                spdlog::info("Fetched words for vocabulary_id {}", vocab_id);
            }
            catch (const ServerExcept&)
            {
                res.status = 400;
                res.set_content(ResponseMessage::error("Invalid vocabulary_id"), "application/json");

                spdlog::error("Invalid vocabulary_id {}", std::stoi(req.matches[1]));
            }
        }

        static void addWordToFavorites(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value body = JsonUtil::deserialize(req.body);
                if (!body.isMember("user_id") || !body.isMember("word_id")) {
                    throw ControllerExcept("user_id and word_id are required in JSON body");
                }
                int user_id = body["user_id"].asInt();
                int word_id = body["word_id"].asInt();

                VocabularyService::instance().addWordToFavorites(user_id, word_id);

                res.status = 200;
                res.set_content(ResponseMessage::success(), "application/json");
                spdlog::info("Added word_id {} to favorites for user_id {}", word_id, user_id);
            }
            catch (const UtilExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error("Invalid JSON format: " + std::string(e.what())), "application/json");
                spdlog::error("JSON parsing error in addWordToFavorites: {}", e.what());
            }
            catch (const ServiceExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Service error in addWordToFavorites: {}", e.what());
            }
            catch (const ControllerExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Controller error in addWordToFavorites: {}", e.what());
            }
        }

        static void removeWordFromFavorites(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value body = JsonUtil::deserialize(req.body);
                if (!body.isMember("user_id") || !body.isMember("word_id")) {
                    throw ControllerExcept("user_id and word_id are required in JSON body");
                }
                int user_id = body["user_id"].asInt();
                int word_id = body["word_id"].asInt();

                VocabularyService::instance().removeWordFromFavorites(user_id, word_id);

                res.status = 200;
                res.set_content(ResponseMessage::success(), "application/json");
                spdlog::info("Removed word_id {} from favorites for user_id {}", word_id, user_id);
            }
            catch (const UtilExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error("Invalid JSON format: " + std::string(e.what())), "application/json");
                spdlog::error("JSON parsing error in removeWordFromFavorites: {}", e.what());
            }
            catch (const ControllerExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Controller error in removeWordFromFavorites: {}", e.what());
            }
            catch (const ServiceExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Service error in removeWordFromFavorites: {}", e.what());
            }
        }

        static void getDefaultVocabulary(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                int user_id = std::stoi(req.get_param_value("user_id"));
                Vocabulary vocabulary = VocabularyService::instance().getDefaultVocabulary(user_id);

                res.status = 200;
                res.set_content(ResponseMessage::success(vocabulary.to_json()), "application/json");

                spdlog::info("Fetched default vocabulary for user_id {}", user_id);
            }
            catch (const ServerExcept& e)
            {
                res.status = 404;
                res.set_content(ResponseMessage::error(e.what()), "application/json");

                spdlog::error("Failed to fetch default vocabulary: {}", e.what());
            }
        }

        static void setDefaultVocabulary(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value json = JsonUtil::deserialize(req.body);

                int user_id = json["user_id"].asInt();
                int vocabulary_id = json["vocabulary_id"].asInt();

                VocabularyService::instance().setDefaultVocabulary(user_id, vocabulary_id);

                res.status = 200;
                res.set_content(ResponseMessage::success(), "application/json");

                spdlog::info("Set default vocabulary {} for user_id {}", vocabulary_id, user_id);
            }
            catch (const ServerExcept& e)
            {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");

                spdlog::error("Failed to set default vocabulary: {}", e.what());
            }
        }

        static void addWordToMistakesBook(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value body = JsonUtil::deserialize(req.body);
                if (!body.isMember("user_id") || !body.isMember("word_id")) {
                    throw ControllerExcept("user_id and word_id are required in JSON body");
                }
                int user_id = body["user_id"].asInt();
                int word_id = body["word_id"].asInt();

                VocabularyService::instance().addWordToMistakesBook(user_id, word_id);

                res.status = 200;
                res.set_content(ResponseMessage::success(), "application/json");
                spdlog::info("Added word_id {} to mistakes book for user_id {}", word_id, user_id);
            }
            catch (const UtilExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error("Invalid JSON: " + std::string(e.what())), "application/json");
                spdlog::error("JSON parsing error in addWordToMistakesBook: {}", e.what());
            }
            catch (const ControllerExcept& e) {
                res.status = 400;
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Controller error in addWordToMistakesBook: {}", e.what());
            }
            catch (const ServiceExcept& e) {
                res.status = 400; // Or other appropriate code like 404 if word not found
                res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Service error in addWordToMistakesBook: {}", e.what());
            }
        }

        static void removeWordFromMistakesBook(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                Json::Value body = JsonUtil::deserialize(req.body);
                if (!body.isMember("user_id") || !body.isMember("word_id")) {
                    throw ControllerExcept("user_id and word_id are required.");
                }
                int user_id = body["user_id"].asInt();
                int word_id = body["word_id"].asInt();

                VocabularyService::instance().removeWordFromMistakesBook(user_id, word_id);
                res.status = 200;
                res.set_content(ResponseMessage::success(), "application/json");
                spdlog::info("Removed word_id {} from mistakes book for user_id {}", word_id, user_id);
            }
            catch (const UtilExcept& e) {
                res.status = 400; res.set_content(ResponseMessage::error("Invalid JSON: " + std::string(e.what())), "application/json");
                spdlog::error("JSON error: {}", e.what());
            }
            catch (const ControllerExcept& e) {
                res.status = 400; res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Controller error: {}", e.what());
            }
            catch (const ServiceExcept& e) {
                res.status = 400; res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Service error: {}", e.what());
            }
        }

        static void getMistakesBookWords(const httplib::Request& req, httplib::Response& res)
        {
            try
            {
                if (!req.has_param("user_id")) {
                    throw ControllerExcept("user_id parameter is required.");
                }
                int user_id = std::stoi(req.get_param_value("user_id"));
                std::vector<Word> words = VocabularyService::instance().getMistakesBookWords(user_id);

                Json::Value data(Json::arrayValue);
                for (const auto& word : words) {
                    data.append(word.to_json()); // Word POCO to_json includes mnemonics array now
                }
                res.status = 200;
                res.set_content(ResponseMessage::success(data), "application/json");
                spdlog::info("Fetched mistakes book words for user_id {}", user_id);

            }
            catch (const std::invalid_argument& e) {
                res.status = 400; res.set_content(ResponseMessage::error("Invalid user_id format."), "application/json");
                spdlog::error("Param error: {}", e.what());
            }
            catch (const ControllerExcept& e) {
                res.status = 400; res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Controller error: {}", e.what());
            }
            catch (const ServiceExcept& e) {
                res.status = 404; res.set_content(ResponseMessage::error(e.what()), "application/json");
                spdlog::error("Service error: {}", e.what());
            }
        }
    };
}